//////////////////////////////////////////ok
#include"stdafx.h"

#include "bochs.h"

struct BxExceptionInfo {
	unsigned	exception_type;
	unsigned	exception_class;
	bx_bool		push_error;
};

#define IA32_ET_BENIGN       0
#define IA32_ET_CONTRIBUTORY 1
#define IA32_ET_PAGE_FAULT   2
#define IA32_EXCEPTION_CLASS_TRAP  0
#define IA32_EXCEPTION_CLASS_FAULT 1
#define IA32_EXCEPTION_CLASS_ABORT 2

#define IA32_ET_DOUBLE_FAULT 10

static const bx_bool is_exception_OK[3][3] = 
{
	{ 1, 1, 1 }, /* 1st exception is BENIGN */
	{ 1, 0, 1 }, /* 1st exception is CONTRIBUTORY */
	{ 1, 0, 0 }  /* 1st exception is PAGE_FAULT */
};

#define BX_CPU_HANDLED_EXCEPTIONS  32

struct BxExceptionInfo exceptions_info[BX_CPU_HANDLED_EXCEPTIONS] = {
	/* DE */ { IA32_ET_CONTRIBUTORY, IA32_EXCEPTION_CLASS_FAULT, 0 },
	/* DB */ { IA32_ET_BENIGN,       IA32_EXCEPTION_CLASS_FAULT, 0 },
	/* 02 */ { IA32_ET_BENIGN,       IA32_EXCEPTION_CLASS_FAULT, 0 }, // NMI
	/* BP */ { IA32_ET_BENIGN,       IA32_EXCEPTION_CLASS_TRAP,  0 },
	/* OF */ { IA32_ET_BENIGN,       IA32_EXCEPTION_CLASS_TRAP,  0 },
	/* BR */ { IA32_ET_BENIGN,       IA32_EXCEPTION_CLASS_FAULT, 0 },
	/* UD */ { IA32_ET_BENIGN,       IA32_EXCEPTION_CLASS_FAULT, 0 },
	/* NM */ { IA32_ET_BENIGN,       IA32_EXCEPTION_CLASS_FAULT, 0 },
	/* DF */ { IA32_ET_DOUBLE_FAULT, IA32_EXCEPTION_CLASS_FAULT, 1 },
	// coprocessor segment overrun (286,386 only)
	/* 09 */ { IA32_ET_BENIGN,       IA32_EXCEPTION_CLASS_FAULT, 0 },
	/* TS */ { IA32_ET_CONTRIBUTORY, IA32_EXCEPTION_CLASS_FAULT, 1 },
	/* NP */ { IA32_ET_CONTRIBUTORY, IA32_EXCEPTION_CLASS_FAULT, 1 },
	/* SS */ { IA32_ET_CONTRIBUTORY, IA32_EXCEPTION_CLASS_FAULT, 1 },
	/* GP */ { IA32_ET_CONTRIBUTORY, IA32_EXCEPTION_CLASS_FAULT, 1 },
	/* PF */ { IA32_ET_PAGE_FAULT,   IA32_EXCEPTION_CLASS_FAULT, 1 },
	/* 15 */ { IA32_ET_BENIGN,       IA32_EXCEPTION_CLASS_FAULT, 0 }, // reserved
	/* MF */ { IA32_ET_BENIGN,       IA32_EXCEPTION_CLASS_FAULT, 0 },
	/* AC */ { IA32_ET_BENIGN,       IA32_EXCEPTION_CLASS_FAULT, 1 },
	/* MC */ { IA32_ET_BENIGN,       IA32_EXCEPTION_CLASS_ABORT, 0 },
	/* XM */ { IA32_ET_BENIGN,       IA32_EXCEPTION_CLASS_FAULT, 0 },
	/* VE */ { IA32_ET_PAGE_FAULT,   IA32_EXCEPTION_CLASS_FAULT, 0 },
	/* 21 */ { IA32_ET_BENIGN,       IA32_EXCEPTION_CLASS_FAULT, 0 },
	/* 22 */ { IA32_ET_BENIGN,       IA32_EXCEPTION_CLASS_FAULT, 0 },
	/* 23 */ { IA32_ET_BENIGN,       IA32_EXCEPTION_CLASS_FAULT, 0 },
	/* 24 */ { IA32_ET_BENIGN,       IA32_EXCEPTION_CLASS_FAULT, 0 },
	/* 25 */ { IA32_ET_BENIGN,       IA32_EXCEPTION_CLASS_FAULT, 0 },
	/* 26 */ { IA32_ET_BENIGN,       IA32_EXCEPTION_CLASS_FAULT, 0 },
	/* 27 */ { IA32_ET_BENIGN,       IA32_EXCEPTION_CLASS_FAULT, 0 },
	/* 28 */ { IA32_ET_BENIGN,       IA32_EXCEPTION_CLASS_FAULT, 0 },
	/* 29 */ { IA32_ET_BENIGN,       IA32_EXCEPTION_CLASS_FAULT, 0 },
	/* 30 */ { IA32_ET_BENIGN,       IA32_EXCEPTION_CLASS_FAULT, 0 }, // FIXME: SVM #SF
	/* 31 */ { IA32_ET_BENIGN,       IA32_EXCEPTION_CLASS_FAULT, 0 }
};


void IA32_CPU::protected_mode_int( Bit32u vector, bx_bool is_INT, bx_bool push_error, Bit32u error_code)
{
	StopReason.klass = ClassExceptionOrInterrupt;
	StopReason.eoi.IsInt = is_INT;
	StopReason.eoi.TrapNo = vector;
	StopReason.eoi.HasErrCode = push_error;
	StopReason.eoi.ErrorCode = error_code;
	longjmp( jmp_buf_env, 1 );

//	Bit32u  dword1, dword2;
//	ia32_descriptor_t gate_descriptor, cs_descriptor;
//	ia32_selector_t cs_selector;
//
//	Bit16u raw_tss_selector;
//	ia32_selector_t   tss_selector;
//	ia32_descriptor_t tss_descriptor;
//
//	Bit16u gate_dest_selector;
//	Bit32u gate_dest_offset;
//
//	if ( (vector*8 + 7) >  idtr.limit) 
//	{
//		exception(IA32_GP_EXCEPTION, vector*8 + 2, 0);
//	}
//
//	m_vm32->access_address_n(this, idtr.base + vector*8,     4, 0, IA32_READ, &dword1);
//	m_vm32->access_address_n(this, idtr.base + vector*8 + 4, 4, 0, IA32_READ, &dword2);
//#ifdef IA32_BIG_ENDIAN
//	IA32_ReadHostDWordFromLittleEndian(&dword1, &dword1);
//	IA32_ReadHostDWordFromLittleEndian(&dword2, &dword2);
//#endif
//	parse_descriptor(dword1, dword2, &gate_descriptor);
//
//	if ((gate_descriptor.valid==0) || gate_descriptor.segment) 
//	{
//		exception(IA32_GP_EXCEPTION, vector*8 + 2, 0);
//	}
//
//	switch (gate_descriptor.type) 
//	{
//	case IA32_TASK_GATE:
//	case IA32_286_INTERRUPT_GATE:
//	case IA32_286_TRAP_GATE:
//	case IA32_386_INTERRUPT_GATE:
//	case IA32_386_TRAP_GATE:
//		break;
//	default:
//		exception(IA32_GP_EXCEPTION, vector*8 + 2, 0);
//		return;
//	}
//
//	if (is_INT && (gate_descriptor.dpl < CPL)) 
//	{
//		exception(IA32_GP_EXCEPTION, vector*8 + 2, 0);
//	}
//
//	if (gate_descriptor.p == 0) 
//	{
//		exception(IA32_NP_EXCEPTION, vector*8 + 2, 0);
//	}
//
//	switch (gate_descriptor.type) 
//	{
//	case IA32_TASK_GATE:
//		raw_tss_selector = gate_descriptor.u.taskgate.tss_selector;
//		parse_selector(raw_tss_selector, &tss_selector);
//		if (tss_selector.ti) 
//		{
//			exception(IA32_TS_EXCEPTION, raw_tss_selector & 0xfffc, 0);
//			return;
//		}
//
//		fetch_raw_descriptor(&tss_selector, &dword1, &dword2, IA32_TS_EXCEPTION);
//		parse_descriptor(dword1, dword2, &tss_descriptor);
//
//		if (tss_descriptor.valid==0 || tss_descriptor.segment) 
//		{
//			exception(IA32_TS_EXCEPTION, raw_tss_selector & 0xfffc, 0);
//			return;
//		}
//
//		if (tss_descriptor.type!=9 && tss_descriptor.type!=1) 
//		{
//			exception(IA32_TS_EXCEPTION, raw_tss_selector & 0xfffc, 0);
//			return;
//		}
//
//		task_switch(&tss_selector, &tss_descriptor,IA32_TASK_FROM_CALL_OR_INT, dword1, dword2);
//		if ( is_error_code ) 
//		{
//			if ( sregs[IA32_SEG_REG_CS].cache.u.segment.d_b)
//				push_32(error_code);
//			else
//				push_16(error_code);
//		}
//
//		if (EIP >  sregs[IA32_SEG_REG_CS].cache.u.segment.limit_scaled) 
//		{
//			exception(IA32_GP_EXCEPTION, 0, 0);
//		}
//		return;
//	case IA32_286_INTERRUPT_GATE:
//	case IA32_286_TRAP_GATE:
//	case IA32_386_INTERRUPT_GATE:
//	case IA32_386_TRAP_GATE:
//		if ( gate_descriptor.type >= 14 ) 
//		{ // 386 gate
//			gate_dest_selector = gate_descriptor.u.gate386.dest_selector;
//			gate_dest_offset   = gate_descriptor.u.gate386.dest_offset;
//		}
//		else 
//		{ // 286 gate
//			gate_dest_selector = gate_descriptor.u.gate286.dest_selector;
//			gate_dest_offset   = gate_descriptor.u.gate286.dest_offset;
//		}
//
//		if ( (gate_dest_selector & 0xfffc) == 0 ) 
//		{
//			exception(IA32_GP_EXCEPTION, 0, 0);
//		}
//
//		parse_selector(gate_dest_selector, &cs_selector);
//		fetch_raw_descriptor(&cs_selector, &dword1, &dword2, IA32_GP_EXCEPTION);
//		parse_descriptor(dword1, dword2, &cs_descriptor);
//
//		if ( cs_descriptor.valid==0 || cs_descriptor.segment==0 || cs_descriptor.u.segment.executable==0 || cs_descriptor.dpl>CPL )
//		{
//			exception(IA32_GP_EXCEPTION, cs_selector.value & 0xfffc, 0);
//		}
//
//		if ( cs_descriptor.p==0 )
//		{
//			exception(IA32_NP_EXCEPTION, cs_selector.value & 0xfffc, 0);
//		}
//
//		if ( cs_descriptor.u.segment.c_ed==0 && cs_descriptor.dpl<CPL )
//		{
//			Bit16u old_SS, old_CS, SS_for_cpl_x;
//			Bit32u ESP_for_cpl_x, old_EIP, old_ESP;
//			ia32_descriptor_t ss_descriptor;
//			ia32_selector_t   ss_selector;
//			int bytes;
//
//			get_SS_ESP_from_TSS(cs_descriptor.dpl,&SS_for_cpl_x, &ESP_for_cpl_x);
//
//			if ( (SS_for_cpl_x & 0xfffc) == 0 ) 
//			{
//				exception(IA32_TS_EXCEPTION, 0, 0); /* TS(ext) */
//			}
//
//			parse_selector(SS_for_cpl_x, &ss_selector);
//			fetch_raw_descriptor(&ss_selector, &dword1, &dword2, IA32_TS_EXCEPTION);
//			parse_descriptor(dword1, dword2, &ss_descriptor);
//
//			if (ss_selector.rpl != cs_descriptor.dpl) 
//			{
//				exception(IA32_TS_EXCEPTION, SS_for_cpl_x & 0xfffc, 0);
//			}
//
//			if (ss_descriptor.dpl != cs_descriptor.dpl) 
//			{
//				exception(IA32_TS_EXCEPTION, SS_for_cpl_x & 0xfffc, 0);
//			}
//
//			if (ss_descriptor.valid==0 || ss_descriptor.segment==0 || ss_descriptor.u.segment.executable==1 || ss_descriptor.u.segment.r_w==0)
//			{
//				exception(IA32_TS_EXCEPTION, SS_for_cpl_x & 0xfffc, 0);
//			}
//
//			if (ss_descriptor.p==0) 
//			{
//				exception(IA32_SS_EXCEPTION, SS_for_cpl_x & 0xfffc, 0);
//			}
//
//			if (gate_descriptor.type>=14) 
//			{
//				if ( is_error_code )
//					bytes = 24;
//				else
//					bytes = 20;
//
//			}
//			else 
//			{
//				if ( is_error_code )
//					bytes = 12;
//				else
//					bytes = 10;
//
//			}
//
//			if ( !can_push(&ss_descriptor, ESP_for_cpl_x, bytes) )
//			{
//				exception(IA32_SS_EXCEPTION, SS_for_cpl_x & 0xfffc, 0);
//			}
//
//			if (gate_dest_offset > cs_descriptor.u.segment.limit_scaled) 
//			{
//				exception(IA32_GP_EXCEPTION, 0, 0);
//			}
//
//			old_ESP = ESP;
//			old_SS  =  sregs[IA32_SEG_REG_SS].selector.value;
//			old_EIP = EIP;
//			old_CS  =  sregs[IA32_SEG_REG_CS].selector.value;
//
//			load_ss(&ss_selector, &ss_descriptor, cs_descriptor.dpl);
//
//			if (ss_descriptor.u.segment.d_b)
//				ESP = ESP_for_cpl_x;
//			else
//				SP = ESP_for_cpl_x; // leave upper 16bits
//
//			load_cs(&cs_selector, &cs_descriptor, cs_descriptor.dpl);
//			EIP = gate_dest_offset;
//
//			Bit32u eflags = read_eflags();
//
//			if ( !(gate_descriptor.type & 1) ) // even is int-gate
//				clear_IF ();
//
//			clear_TF ();
//			clear_VM ();
//			clear_RF ();
//			clear_NT ();
//
//			if (gate_descriptor.type>=14) 
//			{ 
//				push_32(old_SS);
//				push_32(old_ESP);
//
//				// push EFLAGS
//				push_32(eflags);
//
//				// push long pointer to return address onto new stack
//				push_32(old_CS);
//				push_32(old_EIP);
//
//				if ( is_error_code )
//					push_32(error_code);
//			}
//			else 
//			{                          
//				// 286 int/trap gate
//				// push long pointer to old stack onto new stack
//				push_16(old_SS);
//				push_16(old_ESP); // ignores upper 16bits
//
//				// push FLAGS
//				push_16(eflags);  // ignores upper 16bits
//				// push return address onto new stack
//				push_16(old_CS);
//				push_16(old_EIP); // ignores upper 16bits
//
//				if ( is_error_code )
//					push_16(error_code);
//			}
//
//			return;
//		}
//
//
//
//		if ( cs_descriptor.u.segment.c_ed==1 || cs_descriptor.dpl==CPL )
//		{
//			int bytes;
//			Bit32u temp_ESP;
//
//			if ( sregs[IA32_SEG_REG_SS].cache.u.segment.d_b)
//				temp_ESP = ESP;
//			else
//				temp_ESP = SP;
//
//			if (gate_descriptor.type >= 14) 
//			{ 
//				// 386 gate
//				if ( is_error_code )
//					bytes = 16;
//				else
//					bytes = 12;
//			}
//			else 
//			{ // 286 gate
//				if ( is_error_code )
//					bytes = 8;
//				else
//					bytes = 6;
//			}
//
//			if (! can_push(& sregs[IA32_SEG_REG_SS].cache, temp_ESP, bytes))
//			{
//				exception(IA32_SS_EXCEPTION, 0, 0);
//			}
//
//			if (gate_dest_offset > cs_descriptor.u.segment.limit_scaled) 
//			{
//				exception(IA32_GP_EXCEPTION, 0, 0);
//			}
//
//			if (gate_descriptor.type >= 14) 
//			{ // 386 gate
//				push_32(read_eflags());
//				push_32( sregs[IA32_SEG_REG_CS].selector.value);
//				push_32(EIP);
//				if ( is_error_code )
//					push_32(error_code);
//			}
//			else 
//			{ // 286 gate
//				push_16(read_flags());
//				push_16( sregs[IA32_SEG_REG_CS].selector.value);
//				push_16(IP);
//				if ( is_error_code )
//					push_16(error_code);
//			}
//
//			load_cs(&cs_selector, &cs_descriptor, CPL);
//			EIP = gate_dest_offset;
//
//			if ( !(gate_descriptor.type & 1) ) // even is int-gate
//				clear_IF ();
//			clear_TF ();
//			clear_NT ();
//			clear_VM ();
//			clear_RF ();
//			return;
//		}
//
//		exception(IA32_GP_EXCEPTION, cs_selector.value & 0xfffc, 0);
//		break;
//	default:
//		break;
//	}
}

//void IA32_CPU::real_mode_int(Bit8u vector, bx_bool is_INT, bx_bool push_error, Bit16u error_code)
//{
//	Bit16u cs_selector, ip;
//
//	if ( (vector*4+3) >  idtr.limit )
//	{
//		exception(IA32_GP_EXCEPTION, 0, 0);
//	}
//
//	push_16(read_flags());
//	cs_selector =  sregs[IA32_SEG_REG_CS].selector.value;
//	push_16(cs_selector);
//	ip = EIP;
//	push_16(ip);
//	m_vm32->access_address_n(this, idtr.base + 4 * vector,     2, 0, IA32_READ, &ip);
//	m_vm32->access_address_n(this, idtr.base + 4 * vector + 2, 2, 0, IA32_READ, &cs_selector);
//#ifdef IA32_BIG_ENDIAN
//	IA32_ReadHostWordFromLittleEndian(&ip, &ip);
//	IA32_ReadHostWordFromLittleEndian(&cs_selector, &cs_selector);
//#endif
//	EIP = (Bit32u) ip;
//	load_seg_reg(& sregs[IA32_SEG_REG_CS], cs_selector);
//	clear_IF ();
//	clear_TF ();
//	clear_AC ();
//	clear_RF ();
//}

void IA32_CPU::interrupt( Bit32u vector, bx_bool is_INT, bx_bool push_error, Bit32u error_code )
{
	debug_trap = 0;
	inhibit_mask = 0;
	protected_mode_int( vector, is_INT, push_error, error_code);
}

void IA32_CPU::exception( Bit32u vector, Bit32u error_code, bx_bool is_INT )
{
	unsigned exception_type = 0;
	unsigned exception_class = IA32_EXCEPTION_CLASS_FAULT;
	bx_bool push_error = 0;

	if( vector < 0x100 )
		error_code &= 0x0000FFFF;

	if (vector < BX_CPU_HANDLED_EXCEPTIONS)
	{
		push_error = exceptions_info[vector].push_error;
		exception_class = exceptions_info[vector].exception_class;
		exception_type = exceptions_info[vector].exception_type;
		if (vector != IA32_PF_EXCEPTION && vector != IA32_DF_EXCEPTION)
		{
			// Page faults have different format
			error_code = (error_code & 0xfffe) | EXT;
		}
	
		if (exception_class == IA32_EXCEPTION_CLASS_FAULT)
		{
			// restore RIP/RSP to value before error occurred
			EIP = prev_eip;
			ESP = prev_esp;
			//if (BX_CPU_THIS_PTR last_exception_type == IA32_ET_DOUBLE_FAULT)
			//{
			//	longjmp(BX_CPU_THIS_PTR jmp_buf_env, 1); // go back to main decode loop
			//}
			if (vector != IA32_DB_EXCEPTION) assert_RF();
		}

		if (vector == IA32_DB_EXCEPTION) 
		{
			// Commit debug events to DR6: preserve DR5.BS and DR6.BD values,
			// only software can clear them
			dr6 = (dr6 & 0xffff6ff0) | (debug_trap & 0x0000e00f);
			// clear GD flag in the DR7 prior entering debug exception handler
			dr7 |= (1<<13);
		}

		EXT = 1;
	}

	interrupt( vector, 0, push_error, error_code );

	//longjmp(jmp_buf_env, 1); // go back to main decode loop
}
